﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using CLFramework.Infrastructure;

namespace CLFramework.Caching
{

    /// <summary>
    /// 从数据库获取实体对象的方法代理
    /// </summary>
    /// <typeparam name="TKey">实体主键的类型</typeparam>
    /// <typeparam name="TValue">实体的类型</typeparam>
    /// <param name="keys">键集合</param>
    /// <param name="vals">实体集合</param>
    public delegate void GetEntitiesFromDatabaseDelegate<TKey, TValue>(TKey[] keys, ref Dictionary<TKey, TValue> entities);

    /// <summary>
    /// 根据Id集合从CacheEntity中获取数据——首先从缓存获取缓存了的集合，然后从数据库获取未缓存的集合并缓存
    /// </summary>
    /// <typeparam name="TKey"></typeparam>
    /// <typeparam name="TValue"></typeparam>
    /// <param name="query"></param>
    public delegate List<TValue> GetEntitiesFromCacheEntityDelegate<TKey, TValue>(TKey[] ids, ref Dictionary<TKey, TValue> entities, bool entitiesCacheable);

    /// <summary>
    /// 缓存实体管理类
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public class CacheEntityTable<TKey, TValue> : ICacheEntityTable
    {
        int entityCachedSeconds = 180;
        int maxEntityCount = 1000;

        string cacheKey;
        Dictionary<TKey, CacheEntity<TValue>> cachedEntities = new Dictionary<TKey, CacheEntity<TValue>>();
        object syncObj = new object();

        public CacheEntityTable(string cacheKey)
        {
            this.cacheKey = cacheKey;
        }

        public string CacheKey
        {
            get
            {
                return cacheKey;
            }
        }

        public int CacheEntityDuration
        {
            get { return entityCachedSeconds; }
            set { entityCachedSeconds = value; }
        }

        public int MaxEntityCount
        {
            get { return maxEntityCount; }
            set { maxEntityCount = value; }
        }

        public void Cache(TKey key, TValue entity)
        {
            if (entity == null)
                return;

            CacheEntity<TValue> cachedEntity;
            if (!cachedEntities.TryGetValue(key, out cachedEntity))
            {
                cachedEntity = new CacheEntity<TValue>(entity);
                cachedEntity.Key = key;
            }
            cachedEntity.Object = entity;
            lock (syncObj)
            {
                cachedEntities[key] = cachedEntity;
            }

        }

        public int KeysCount
        {
            get
            {

                return cachedEntities.Keys.Count;
            }
        }

        public int ValuesCount
        {
            get
            {
                return cachedEntities.Values.Count;
            }
        }

        public TValue Get(TKey key)
        {
            TValue value;
            TryGetValue(key, out value);
            return value;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public bool TryGetValue(TKey key, out TValue value)
        {
            CacheEntity<TValue> cachedEntity;
            bool exists = cachedEntities.TryGetValue(key, out cachedEntity);
            if (exists)
            {
                value = cachedEntity.Object;
            }
            else
                value = default(TValue);
            return exists;
        }

        public bool ContainsKey(TKey key)
        {
            return cachedEntities.ContainsKey(key);
        }

        public List<TKey> Keys
        {
            get
            {
                return cachedEntities.Keys.ToList();
            }
        }
        //add by: leoliao 
        //on: 2012-05-15
        public List<TValue> Values
        {
            get
            {
                return cachedEntities.Values.Select(c=>c.Object).ToList();
            }
        }

        public void Remove(TKey key)
        {
            lock (syncObj)
            {
                cachedEntities.Remove(key);
            }
        }

        public void RemoveByValue(TValue value)
        {
            var key = cachedEntities.Where(kv => kv.Value.Object.Equals(value)).FirstOrDefault();
            if (!key.Key.Equals(default(TKey)))
                lock (syncObj)
                    cachedEntities.Remove(key.Key);
        }

        public void Remove(TKey[] keys)
        {
            foreach (var key in keys)
            {
                Remove(key);
            }
        }

        public void RemoveExpiredEntity(TKey key, DateTime timeStamp)
        {
            CacheEntity<TValue> cachedEntity;
            if (cachedEntities.TryGetValue(key, out cachedEntity))
            {
                if (cachedEntity.Created < timeStamp)
                {
                    lock (syncObj)
                    {
                        cachedEntities.Remove(key);
                    }
                }
            }
        }

        public void RemoveExpiredEntities(Dictionary<TKey, DateTime> timeStamps)
        {
            CacheEntity<TValue> tv;
            List<TKey> ks;
            List<TKey> tobeRemoveKeys = new List<TKey>();

            if (timeStamps != null)
            {
                ks = new List<TKey>(timeStamps.Keys);
                foreach (TKey key in ks)
                {
                    if (cachedEntities.TryGetValue(key, out tv))
                    {
                        if (tv.Created < timeStamps[key])
                        {
                            tobeRemoveKeys.Add(key);
                        }
                    }
                }
            }

            if (tobeRemoveKeys.Count > 0)
            {
                RemoveEntities(tobeRemoveKeys.ToArray());
            }
        }

        /// <summary>
        /// 移除过期的缓存项
        /// </summary>
        public void RemoveExpiredEntities()
        {
            CacheEntity<TValue> tv;
            List<TKey> ks;
            List<TKey> tobeRemoveKeys = new List<TKey>();

            // EntityCachedSeconds为0表示长效缓存
            //
            if (entityCachedSeconds > 0)
            {
                ks = new List<TKey>(cachedEntities.Keys);
                foreach (TKey key in ks)
                {
                    if (cachedEntities.TryGetValue(key, out tv))
                    {
                        if (tv.LastActivity < DateTime.UtcNow.AddSeconds(-entityCachedSeconds))
                        {
                            tobeRemoveKeys.Add(key);
                        }
                    }
                }
            }

            if (tobeRemoveKeys.Count > 0)
            {
                RemoveEntities(tobeRemoveKeys.ToArray());
            }

            if (cachedEntities.Count > MaxEntityCount)
            {
                List<CacheEntity<TValue>> objs = new List<CacheEntity<TValue>>(cachedEntities.Values);
                objs.Sort();
                int len = cachedEntities.Count - MaxEntityCount;
                CacheEntity<TValue>[] arr = new CacheEntity<TValue>[len];
                objs.CopyTo(0, arr, 0, len);

                lock (syncObj)
                {
                    foreach (CacheEntity<TValue> obj in arr)
                    {
                        TKey key = (TKey)obj.Key;
                        cachedEntities.Remove(key);
                    }
                }
            }
        }

        public void RemoveEntities(TKey[] tobeRemoveKeys)
        {
            lock (syncObj)
            {
                foreach (TKey key in tobeRemoveKeys)
                {
                    cachedEntities.Remove(key);
                }
            }
        }

        public List<TValue> GetEntities(TKey[] keys, ref Dictionary<TKey, TValue> entitiesTable, bool cacheable,
            GetEntitiesFromDatabaseDelegate<TKey, TValue> getEntitiesFromDatabase)
        {
            return GetEntities(keys, ref entitiesTable, cacheable, getEntitiesFromDatabase, true);
        }

        /// <summary>
        /// 根据Key集合获取实体集合
        /// </summary>
        /// <param name="postIds"></param>
        /// <returns></returns>
        public List<TValue> GetEntities(TKey[] keys, ref Dictionary<TKey, TValue> entitiesTable, bool cacheable,
            GetEntitiesFromDatabaseDelegate<TKey, TValue> getEntitiesFromDatabase, bool refEntitiesTable)
        {
            List<TValue> entities = new List<TValue>();
            if (entitiesTable == null)
                entitiesTable = new Dictionary<TKey, TValue>();

            List<TKey> uncachedKeys = new List<TKey>(); // 缓存中没有的key列表

            TValue entity;

            // 先检索缓存
            //
            foreach (TKey key in keys)
            {
                entity = default(TValue);
                CacheEntity<TValue> tv;

                if (cacheable)
                {
                    if (cachedEntities.TryGetValue(key, out tv))
                        entity = tv.Object;
                }

                if (entity != null)
                {
                    entitiesTable[key] = entity;
                }
                else
                    uncachedKeys.Add(key);
            }

            // 如果有没有被Cache的项，从数据库中读取
            //
            if (uncachedKeys.Count > 0)
            {
                if (refEntitiesTable)
                {
                    getEntitiesFromDatabase(uncachedKeys.ToArray(), ref entitiesTable); // 填充数据
                }
                else
                {
                    Dictionary<TKey, TValue> entitiesTableFromDb = new Dictionary<TKey, TValue>();
                    getEntitiesFromDatabase(uncachedKeys.ToArray(), ref entitiesTableFromDb); // 填充数据
                    foreach (TKey key in entitiesTableFromDb.Keys)
                    {
                        entitiesTable[key] = entitiesTableFromDb[key];
                    }
                }
            }

            foreach (TKey key in keys)
            {
                // 如果key对应的实体不存在，跳过
                //
                if (entitiesTable.TryGetValue(key, out entity))
                {
                    entities.Add(entity);
                }
                else
                    entity = default(TValue); // 如果DB中不存在，依然记录到缓存表中，避免不存在的ID会绕过缓存重复读取数据库
                //缓存
                if (cacheable)
                    Cache(key, entity);
            }
            return entities;
        }

        public PagedResult<TValue> GetEntitySet(List<TKey> keys, int pageIndex, int pageSize
            , bool entitiesCacheable, GetEntitiesFromDatabaseDelegate<TKey, TValue> getEntitiesFromDatabase)
        {
            Dictionary<TKey, TValue> placesTable = new Dictionary<TKey, TValue>();
            List<TValue> list = GetEntities(PageHelper.GetIds<TKey>(keys, pageIndex, pageSize), ref placesTable, entitiesCacheable,
                getEntitiesFromDatabase);

            PagedResult<TValue> set = new PagedResult<TValue>(list, pageIndex, pageSize, keys.Count);
            return set;
        }


        #region ICacheEntityTable Members


        public void RemoveEntitiesByTimeStamp(List<CacheEntityTimeStamp> stamps)
        {

        }

        public void RemoveAll()
        {
            lock (syncObj)
            {
                cachedEntities = new Dictionary<TKey, CacheEntity<TValue>>();
            }
        }

        public string CacheType
        {
            get
            {
                return string.Format("{0}-->{1}", typeof(TKey), typeof(TValue));
            }
        }

        #endregion
    }

}
